//
// See the file COPYING for redistribution information.
//

#ifndef __NAIPAX_ARES_HPP__
#define __NAIPAX_ARES_HPP__

#include <string>
#include <vector>
#include <stdint.h>
#include <ares.h>
#include <time.h>
#include <sys/types.h>
#include <Naipax/Common.hpp>
#include <Naipax/Cache.hpp>

namespace Naipax {
    // Note that this is a class is totally transparent. Please make
    // sure that you instantiate objects using the constructor.
    class AresData {
    public:
        /** Constructor
         *
         * It calls Build() to set up object.
         */
        AresData(const char *pHostname, size_t HostnameLength);

        /// Constructor
        AresData();

        /** Set up object.
         *
         * @param pHostname
         * @param HostnameLength The length of pHostname
         * @exception Error::INVALID_ARGUMENT if pHostname is NULL
         * @exception Error::DATA_OUT_OF_RANGE if the length of pHostname
         *            exceeds HOSTNAME_SIZE - 1.
         */
        void Build(const char *pHostname, size_t HostnameLength);

        uint32_t mIndex;
        bool mIsValid;
        bool mIsDone;
        int32_t mAresReturn;
        time_t mExpireTime;
        char mHostname[Naipax::HOSTNAME_SIZE];
        int32_t mTtl;
        bool mIsIpV6;
        struct in_addr mIpV4Addr;
        struct sockaddr_in6 mIpV6Addr;
    };

    /** Asynchronous domain name resolver
     * 
     * This is a specialized wrapper around c-ares library. It queries
     * only for addresses, no MX records.
     *
     * Please see also ARES://daniel.haxx.se/projects/c-ares/
     */
    class Ares {
    public:
        /** Constructor
         *
         * @param ConnCount The number of DNS query connections.
         * @exception CANNOT_INIT_ARES if any error occurs.
         */
        Ares(uint32_t ConnCount = 10);

        /// Destructor
        ~Ares();

        /** Submit asynchronous DNS query
         *
         * @param pDomainName The domain name to be resolved.
         * @return 0 if query is submitted successfully.
         */
        int32_t Submit(const AresData &rData);

        /** Check if there are unresolved queries.
         *
         * @return true if there are.
         * @return false otherwise.
         */
        bool HasUnresolved();

        /** Check if a previous query has results
         *
         * @param rDataVector A vector containing query results.
         * @return true if there is a result for a previous query.
         * @return false otherwise
         */
        bool HasResult(std::vector<AresData> &rDataVector);

        /** Cancel all pending DNS queries
         */
        void Cancel();

        AresData* DataArray() { return _mpDataArray; }

    private:
        uint32_t _mConnCount;
        ares_channel *_mpAresChannels;
        uint32_t _mCurAresChannel;
        AresData *_mpDataArray;
        Cache *_mpAresDataCache;
        std::vector<AresData> _mAresDataVector;
        fd_set _mReadFdSet;
        fd_set _mWriteFdSet;
    };
};

#endif // __NAIPAX_ARES_HPP__
